package net.cassite.pure.aop;

import net.cassite.pure.ioc.AutoWire;
import net.cassite.pure.ioc.Scope;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static net.cassite.style.Style.*;
import static net.cassite.style.aggregation.Aggregation.*;

/**
 * Controls AOP processes
 *
 * @author wkgcass
 * @since 0.1.1
 */
public abstract class AOPController {

        private static final Logger LOGGER = LoggerFactory.getLogger(AOPController.class);

        /**
         * retrieve proxy object
         *
         * @param scope ioc scope
         * @param obj     the object to enable aop on
         * @param <T>     target object type
         * @return proxy object
         */
        @SuppressWarnings("unchecked")
        public static <T> T weave(Scope scope, T obj) {
                if (scope == null) {
                        scope = new Scope();
                }
                final Scope s = scope;
                LOGGER.debug("Weaving object {}", obj);
                AOP aop = obj.getClass().getAnnotation(AOP.class);
                Weaver[] weavers;
                boolean useCglib;
                if (aop == null) {
                        return obj;
                } else {
                        weavers = new Weaver[aop.value().length];
                        useCglib = aop.useCglib();
                        $(aop.value()).forEach((e, i) -> {
                                Weaver w = (Weaver) AutoWire.get(s, e);
                                if (w instanceof TargetAware) {
                                        ((TargetAware<Object>) w).targetAware(obj);
                                }
                                weavers[$(i)] = w;
                        });
                }
                LOGGER.debug("retrieved weavers are {}", (Object) weavers);
                if (useCglib || obj.getClass().getInterfaces() == null || obj.getClass().getInterfaces().length == 0) {
                        CglibHandler h = new CglibHandler(weavers, obj);
                        return (T) h.proxy();
                } else {
                        Handler h = new Handler(weavers, obj);
                        return (T) h.proxy();
                }
        }

        /**
         * retrieve proxy object
         *
         * @param obj the object to enable aop on
         * @param <T> target object type
         * @return proxy object
         */
        public static <T> T weave(T obj) {
                return weave(null, obj);
        }
}
